package de.stefanocke.roo.wicket.persistence

import de.stefanocke.roo.wicket.Stereotypes
import de.stefanocke.roo.wicket.base.WicketItdBuilder
import java.lang.reflect.Modifier
import java.util.List
import org.springframework.roo.classpath.details.FieldMetadataBuilder
import org.springframework.roo.model.JavaType

import static de.stefanocke.roo.xtend.Method.*
import static de.stefanocke.roo.xtend.MethodSignature.*
import static de.stefanocke.roo.xtend.Parameter.*

import static extension de.stefanocke.roo.xtend.RooExtensions.*

class PersistenceMetadataItdBuilder extends WicketItdBuilder {
	override PersistenceBindingMetadata getMetadata(){
		super.metadata as PersistenceBindingMetadata
	} 
	
	override String baseClassStereotype(){
		Stereotypes::PERSISTENCE_BINDINGS
	}
	
	override JavaType getBaseClass(){
		super.baseClass?.fqn.javaType(modelClass, idType)
	}
	
	def getPersistenceDetails(){
		metadata.persistenceDetails
	}
	def getIdAccessor(){
		persistenceDetails?.getIdentifierAccessorMethod
	}
	def getVersionAccessor(){
		persistenceDetails?.getVersionAccessorMethod
	}
	def getFindMethod(){
		persistenceDetails?.getFindMethod
	}
	def getPersistMethod(){
		persistenceDetails?.getPersistMethod
	}
	def getRemoveMethod(){
		persistenceDetails?.getRemoveMethod
	}
	def getIdType(){
		getIdAccessor?.returnType
	}
	def getVersionType(){
		versionAccessor?.returnType
	}
	
	override getMemberTypeAdditions(){
		newArrayList(query, countQuery)
	}
	
	def query(){
		metadata.getQueryMetadata
	}
	
	def queryMethodName(){
		query.methodName
	}
	
	def customQueryParams(){
		metadata.getCustomQueryParams
	}
	
	def queryInvocation(){
		'''«modelClass.resolveStatic».«queryMethodName»'''	
	}
	
	def countQuery(){
		metadata.getCountQueryMetadata
	}
	
	def isFindEntriesMethod(){
		!metadata.isCustomQuery
	}
	
	def isCustomQuery(){!isFindEntriesMethod}
	
//	override List<FieldMetadataBuilder> getFields(){	
//		if(customQuery){
//			customQueryParams.map(p | modelField(p.type, p.name))			
//		} else {newArrayList()}
//	}
		
	override getMethods(){
		var methods = constructors 
		
		//methods += detachMethod
		methods += iteratorMethod
		methods += sizeMethod
		methods += loadMethod
		methods += getIdMethod
		methods += saveMethod
		methods += deleteMethod
		methods
	}

	def getConstructors(){
		newArrayList(
			method(
				signature(),
				[|'''
				super();
				«callInjectorIfRequired»
				''']
			)
		)
	}
	
	

	
//	@Override
//	public void detach() {
//		
//	}
//	def getDetachMethod(){
//		method(
//			signature(Modifier::PUBLIC, 
//				JavaType::VOID_PRIMITIVE, 
//				"detach"),
//			[|'''
//			«FOR p : customQueryParams»
//				«p.name».detach();
//			«ENDFOR»
//			return;
//			''']
//		)
//	}

	def firstParam(){
		param(PersistenceMetadataServiceImpl::FIRST_RESULT_PARAMETER)
	}
	
	def sizeParam(){
		param(PersistenceMetadataServiceImpl::MAX_RESULTS_PARAMETER)
	}

//	@Override
//	public Iterator<? extends Post> iterator(int first, int count, ISortState<String> sortState) {
//		return Post.findPostEntries(first, count).iterator();
//	}

	def getIteratorMethod(){
		method(
			signature(Modifier::PUBLIC, 
				"java.util.Iterator".javaType(modelClass), 
				"iterator", 
				param(JavaType::LONG_PRIMITIVE, "first_"),
				param(JavaType::LONG_PRIMITIVE, "size_")),
				

			[|{
			if(isFindEntriesMethod) 
			'''
			int «firstParam.name» = (int) first_;
			int «sizeParam.name» = (int) size_;
			return «query.methodCall».iterator();
			'''
			else
			'''
			return «queryInvocation»(«FOR p : customQueryParams SEPARATOR ", "»«p.name».getObject()«ENDFOR»)
				.setFirstResult((int)«firstParam.name»)
				.setMaxResults((int)«sizeParam.name»)
				.getResultList().iterator();
			'''
			}]
		)
	}
	

//	@Override
//	public int size() {
//		return (int) Post.countPosts();
//	}
//
	def getSizeMethod(){
		method(
			signature(Modifier::PUBLIC, JavaType::LONG_PRIMITIVE, "size"),
				//TODO: Use finder method metadata
			[|
			if(countQuery!=null)
			'''
			return «countQuery.methodCall»;
			'''
			else
			//If we have no count query, we call the finder and return the size of the result set.
			//This is inefficient, but unfortunately, Roo currently does not generate
			//count queries for dynamic fnders...
			'''
			return «queryInvocation»(«FOR p : customQueryParams SEPARATOR ", "»«p.name».getObject()«ENDFOR»)
				.getResultList().size();
			'''
			]
		)
	}

//	@Override
//	public IModel<Post> model(Post object) {
//		return new PostModel(object);
//	}
//	
//	def getModelMethod(){
//		method(
//			signature(Modifier::PUBLIC, 
//				"org.apache.wicket.model.IModel".javaType(modelClass), 
//				"model", param(modelClass,"entity")),
//			[|'''
//			return new «metadata.getModelMetadata.destination.resolve»(entity);
//			''']
//		)
//	}


	//@Override
	//	protected Long getId(Post entity) {
	//		return entity.getId();
	//	}
	//

	def getGetIdMethod(){
		method(
			signature(Modifier::PUBLIC, idType, "getId", param(modelClass,"entity")),
			[|'''
			return entity.«getIdAccessor.methodName»();
			''']
		)
	}

	def idParam(){
		param (idType, PersistenceMetadataServiceImpl::ID_PARAMETER_NAME)
	}
//		@Override
//	protected Whole load(Long id) {
//		return Whole.findWhole(id);
//	}

	def getLoadMethod(){
		method(
			signature(Modifier::PUBLIC, modelClass, "load", idParam),
			[|'''
			return «findMethod.methodCall»;
			''']
		)
	}

	
	def entityParameter(){
			param(PersistenceMetadataServiceImpl::getEntityParameter(modelClass));
		}
	
	//	@Override
	//	protected Whole save(Whole entity) {
	//		entity.persist();
	//		return entity;
	//	}
	def getSaveMethod(){
		method(
			signature(
				Modifier::PUBLIC, modelClass, "save", entityParameter
			),
			[|'''
			«persistMethod.methodCall»;
			return «entityParameter.name»;
			''']
			
		)
	}


	//
	//	@Override
	//	protected void delete(Whole entity) {
	//		entity.remove();
	//	}
	def getDeleteMethod(){
		method(
			signature(
				Modifier::PUBLIC, JavaType::VOID_PRIMITIVE, "delete", entityParameter
			),
			[|'''
			«removeMethod.methodCall»;
			''']
			
		)
	}


}